import os
import re
import ast
import json
import tempfile
import subprocess
from datetime import datetime
from typing import List, Dict, Any, Optional, Tuple
from pathlib import Path
import logging
from rich.console import Console
from rich.logging import RichHandler
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.panel import Panel
from rich.table import Table

# 初始化Rich控制台
console = Console()

# 配置日志
def setup_logging(level: str = "INFO"):
    """设置Rich格式的日志"""
    logging.basicConfig(
        level=getattr(logging, level.upper()),
        format="%(message)s",
        datefmt="[%X]",
        handlers=[RichHandler(console=console, rich_tracebacks=True)]
    )
    return logging.getLogger("CodeBuddy")

logger = setup_logging()

class CodeAnalyzer:
    """代码分析工具类"""
    
    @staticmethod
    def get_file_type(filename: str) -> str:
        """根据文件名判断文件类型"""
        ext = filename.lower().split('.')[-1]
        type_map = {
            'py': 'python',
            'js': 'javascript',
            'ts': 'typescript',
            'java': 'java',
            'cpp': 'cpp',
            'c': 'c',
            'h': 'cpp',
            'hpp': 'cpp',
            'cs': 'csharp',
            'go': 'go',
            'rs': 'rust',
            'php': 'php',
            'rb': 'ruby',
            'swift': 'swift',
            'kt': 'kotlin',
            'scala': 'scala',
            'html': 'html',
            'css': 'css',
            'sql': 'sql',
            'md': 'markdown',
            'json': 'json',
            'xml': 'xml',
            'yaml': 'yaml',
            'yml': 'yaml',
            'sh': 'shell',
            'bash': 'shell',
            'zsh': 'shell',
            'ps1': 'powershell',
            'bat': 'batch',
            'cmd': 'batch'
        }
        return type_map.get(ext, 'unknown')
    
    @staticmethod
    def analyze_code(code: str, file_type: str = 'python') -> Dict[str, Any]:
        """分析代码，返回统计信息"""
        try:
            # 根据文件类型选择分析方法
            if file_type in ['py', 'python']:
                return CodeAnalyzer._analyze_python_code(code)
            elif file_type in ['js', 'javascript', 'ts', 'typescript']:
                return CodeAnalyzer._analyze_js_code(code)
            elif file_type in ['html', 'css', 'xml']:
                return CodeAnalyzer._analyze_markup_code(code)
            elif file_type in ['java', 'cpp', 'cs']:
                return CodeAnalyzer._analyze_static_code(code)
            else:
                return CodeAnalyzer._analyze_generic_code(code)
        except Exception as e:
            logger.error(f"代码分析失败: {e}")
            return {'error': str(e)}
    
    @staticmethod
    def _analyze_python_code(code: str) -> Dict[str, Any]:
        """分析Python代码"""
        try:
            cleaned_code = CodeAnalyzer._clean_code(code, 'python')
            if not cleaned_code.strip():
                return {'error': 'Empty code'}
            
            # 尝试解析代码
            try:
                tree = ast.parse(cleaned_code)
            except SyntaxError as e:
                # 如果解析失败，尝试修复常见问题
                cleaned_code = CodeAnalyzer._fix_python_syntax(cleaned_code)
                try:
                    tree = ast.parse(cleaned_code)
                except SyntaxError as e:
                    logger.error(f"Python代码分析失败: {e}")
                    return {'error': str(e)}
            
            stats = {
                'lines': len(cleaned_code.split('\n')),
                'functions': 0,
                'classes': 0,
                'imports': 0,
                'complexity_score': 0
            }
            
            for node in ast.walk(tree):
                if isinstance(node, ast.FunctionDef):
                    stats['functions'] += 1
                elif isinstance(node, ast.ClassDef):
                    stats['classes'] += 1
                elif isinstance(node, (ast.Import, ast.ImportFrom)):
                    stats['imports'] += 1
                elif isinstance(node, (ast.If, ast.For, ast.While, ast.Try)):
                    stats['complexity_score'] += 1
            
            return stats
        except Exception as e:
            logger.error(f"Python代码分析失败: {e}")
            return {'error': str(e)}
    
    @staticmethod
    def _fix_python_syntax(code: str) -> str:
        """修复Python语法问题"""
        # 修复缩进问题
        lines = code.split('\n')
        fixed_lines = []
        current_indent = 0
        
        for line in lines:
            # 跳过空行
            if not line.strip():
                fixed_lines.append('')
                continue
            
            # 计算当前行的缩进级别
            indent = len(line) - len(line.lstrip())
            
            # 如果缩进级别不合理，进行调整
            if indent % 4 != 0:
                indent = (indent // 4) * 4
            
            # 添加修复后的行
            fixed_lines.append(' ' * indent + line.lstrip())
        
        return '\n'.join(fixed_lines)
    
    @staticmethod
    def _analyze_js_code(code: str) -> Dict[str, Any]:
        """分析JavaScript/TypeScript代码"""
        try:
            cleaned_code = CodeAnalyzer._clean_code(code, 'javascript')
            if not cleaned_code.strip():
                return {'error': 'Empty code'}

            stats = {
                'lines': len(cleaned_code.split('\n')),
                'functions': len(re.findall(r'(?:function\s+\w+\s*\(|const\s+\w+\s*=\s*(?:async\s*)?\([^)]*\)\s*=>|let\s+\w+\s*=\s*(?:async\s*)?\([^)]*\)\s*=>)', cleaned_code)),
                'classes': len(re.findall(r'class\s+\w+', cleaned_code)),
                'imports': len(re.findall(r'(?:import\s+.*from|require\s*\(|import\s*\(|import\s*\{)', cleaned_code)),
                'complexity_score': len(re.findall(r'(?:if\s*\(|for\s*\(|while\s*\(|switch\s*\(|try\s*\{|catch\s*\()', cleaned_code))
            }
            return stats
        except Exception as e:
            logger.error(f"JavaScript代码分析失败: {e}")
            return {'error': str(e)}
    
    @staticmethod
    def _analyze_static_code(code: str) -> Dict[str, Any]:
        """分析静态类型语言代码（Java/C++/C#）"""
        try:
            # 清理代码
            code = CodeAnalyzer._clean_code(code)
            if not code.strip():
                return {'error': 'Empty code'}

            stats = {
                'lines': len(code.split('\n')),
                'functions': len(re.findall(r'(?:\w+\s+)?\w+\s+\w+\s*\([^)]*\)\s*(?:throws\s+\w+)?\s*{', code)),
                'classes': len(re.findall(r'class\s+\w+', code)),
                'imports': len(re.findall(r'(?:import\s+|#include\s+|using\s+)', code)),
                'complexity_score': len(re.findall(r'(?:if\s*\(|for\s*\(|while\s*\(|switch\s*\(|try\s*\{|catch\s*\()', code))
            }
            return stats
        except Exception as e:
            logger.error(f"静态语言代码分析失败: {e}")
            return {'error': str(e)}
    
    @staticmethod
    def _analyze_markup_code(code: str) -> Dict[str, Any]:
        """分析标记语言代码（HTML/CSS/XML）"""
        try:
            cleaned_code = CodeAnalyzer._clean_code(code, 'html')
            if not cleaned_code.strip():
                return {'error': 'Empty code'}

            stats = {
                'lines': len(cleaned_code.split('\n')),
                'elements': len(re.findall(r'<[^>]+>', cleaned_code)),
                'attributes': len(re.findall(r'\w+="[^"]*"', cleaned_code)),
                'complexity_score': len(re.findall(r'<[^>]+>', cleaned_code)) // 2
            }
            return stats
        except Exception as e:
            logger.error(f"标记语言代码分析失败: {e}")
            return {'error': str(e)}
    
    @staticmethod
    def _analyze_generic_code(code: str) -> Dict[str, Any]:
        """通用代码分析"""
        try:
            # 清理代码
            code = CodeAnalyzer._clean_code(code)
            if not code.strip():
                return {'error': 'Empty code'}

            stats = {
                'lines': len(code.split('\n')),
                'complexity_score': len(re.findall(r'(?:if|for|while|switch|try|catch)', code))
            }
            return stats
        except Exception as e:
            logger.error(f"通用代码分析失败: {e}")
            return {'error': str(e)}
    
    @staticmethod
    def count_complexity(code: str, file_type: str = 'python') -> Dict[str, int]:
        """计算代码复杂度指标"""
        return CodeAnalyzer.analyze_code(code, file_type)
    
    @staticmethod
    def extract_functions(code: str, file_type: str = 'python') -> List[Dict[str, Any]]:
        """提取代码中的函数信息"""
        try:
            if file_type == 'python':
                return CodeAnalyzer._extract_python_functions(code)
            elif file_type in ['javascript', 'typescript']:
                return CodeAnalyzer._extract_js_functions(code)
            elif file_type in ['java', 'cpp', 'csharp']:
                return CodeAnalyzer._extract_static_functions(code)
            else:
                return []
        except Exception as e:
            logger.error(f"函数提取失败: {e}")
            return []
    
    @staticmethod
    def extract_classes(code: str, file_type: str = 'python') -> List[Dict[str, Any]]:
        """提取代码中的类信息"""
        try:
            if file_type == 'python':
                return CodeAnalyzer._extract_python_classes(code)
            elif file_type in ['javascript', 'typescript']:
                return CodeAnalyzer._extract_js_classes(code)
            elif file_type in ['java', 'cpp', 'csharp']:
                return CodeAnalyzer._extract_static_classes(code)
            else:
                return []
        except Exception as e:
            logger.error(f"类提取失败: {e}")
            return []
    
    @staticmethod
    def _clean_code(code: str, file_type: str = 'python') -> str:
        """清理代码，移除markdown标记和其他非代码内容"""
        if not code:
            return ""
        
        # 移除markdown代码块标记
        code = re.sub(r'```(?:python|javascript|js|html|css|bash)?\s*', '', code)
        code = re.sub(r'```\s*$', '', code)
        
        # 移除文件路径注释
        code = re.sub(r'# === .* ===\s*', '', code)
        code = re.sub(r'<!-- === .* ===\s*', '', code)
        
        # 移除空行和多余的空格
        lines = []
        in_code_block = False
        in_docstring = False
        docstring_type = None
        
        for line in code.split('\n'):
            line = line.rstrip()
            
            # 跳过markdown标题和列表
            if line.startswith('#') or line.startswith('-') or line.startswith('*'):
                continue
                
            # 跳过markdown代码块标记
            if line.startswith('```'):
                in_code_block = not in_code_block
                continue
                
            # 如果不在代码块中，跳过
            if not in_code_block and not line.strip():
                continue
                
            # 处理Python文档字符串
            if file_type in ['python', 'py']:
                # 检查是否开始或结束文档字符串
                if '"""' in line:
                    if not in_docstring:
                        in_docstring = True
                        docstring_type = '"""'
                    elif docstring_type == '"""':
                        in_docstring = False
                        continue
                elif "'''" in line:
                    if not in_docstring:
                        in_docstring = True
                        docstring_type = "'''"
                    elif docstring_type == "'''":
                        in_docstring = False
                        continue
                
                # 如果在文档字符串中，跳过该行
                if in_docstring:
                    continue
                
                # 移除Python风格的注释
                if line.startswith('#') and ('===' in line or 'path=' in line):
                    continue
            
            # 替换全角字符为半角字符
            char_map = {
                '，': ',', '。': '.', '：': ':', '（': '(', '）': ')',
                '【': '[', '】': ']', '「': '"', '」': '"', '、': ',',
                '；': ';', '？': '?', '！': '!', '＝': '=', '＋': '+',
                '－': '-', '＊': '*', '／': '/', '％': '%', '＆': '&',
                '｜': '|', '＜': '<', '＞': '>', '～': '~', '＾': '^',
                '＠': '@', '＃': '#', '＄': '$', '＿': '_', '｛': '{',
                '｝': '}', '｀': '`', '＼': '\\', '　': ' '
            }
            for full_width, half_width in char_map.items():
                line = line.replace(full_width, half_width)
            
            # 保留所有非空行，包括只有空格的行
            if line.strip() or line.startswith(' '):
                lines.append(line)
        
        # 修复缩进问题
        fixed_lines = []
        current_indent = 0
        for line in lines:
            # 计算当前行的缩进级别
            indent = len(line) - len(line.lstrip())
            # 如果缩进级别不合理，进行调整
            if indent % 4 != 0:
                indent = (indent // 4) * 4
            # 添加修复后的行
            fixed_lines.append(' ' * indent + line.lstrip())
        
        # 移除开头的空行
        while fixed_lines and not fixed_lines[0].strip():
            fixed_lines.pop(0)
        
        # 移除结尾的空行
        while fixed_lines and not fixed_lines[-1].strip():
            fixed_lines.pop()
        
        return '\n'.join(fixed_lines)
    
    @staticmethod
    def _extract_python_functions(code: str) -> List[Dict[str, Any]]:
        """提取Python代码中的函数信息"""
        try:
            cleaned_code = CodeAnalyzer._clean_code(code)
            if not cleaned_code.strip():
                return []
                
            tree = ast.parse(cleaned_code)
            functions = []
            
            for node in ast.walk(tree):
                if isinstance(node, ast.FunctionDef):
                    func_info = {
                        'name': node.name,
                        'args': [arg.arg for arg in node.args.args],
                        'lineno': node.lineno,
                        'docstring': ast.get_docstring(node),
                        'decorators': [d.id if hasattr(d, 'id') else str(d) for d in node.decorator_list]
                    }
                    functions.append(func_info)
            
            return functions
        except Exception as e:
            logger.error(f"函数提取失败: {e}")
            return []
    
    @staticmethod
    def _extract_python_classes(code: str) -> List[Dict[str, Any]]:
        """提取Python代码中的类信息"""
        try:
            cleaned_code = CodeAnalyzer._clean_code(code)
            if not cleaned_code.strip():
                return []
                
            tree = ast.parse(cleaned_code)
            classes = []
            
            for node in ast.walk(tree):
                if isinstance(node, ast.ClassDef):
                    class_info = {
                        'name': node.name,
                        'methods': [],
                        'lineno': node.lineno,
                        'docstring': ast.get_docstring(node),
                        'bases': [base.id if hasattr(base, 'id') else str(base) for base in node.bases]
                    }
                    
                    # 提取方法
                    for item in node.body:
                        if isinstance(item, ast.FunctionDef):
                            class_info['methods'].append(item.name)
                    
                    classes.append(class_info)
            
            return classes
        except Exception as e:
            logger.error(f"类提取失败: {e}")
            return []

class FileManager:
    """文件管理工具类"""
    
    @staticmethod
    def safe_write_file(filepath: str, content: str, backup: bool = True) -> bool:
        """安全写入文件，支持备份"""
        try:
            path = Path(filepath)
            
            # 创建目录
            path.parent.mkdir(parents=True, exist_ok=True)
            
            # 备份原文件
            if backup and path.exists():
                backup_path = path.with_suffix(f'.backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}{path.suffix}')
                path.rename(backup_path)
                logger.info(f"📁 已备份原文件: {backup_path}")
            
            # 写入新内容
            with open(path, 'w', encoding='utf-8') as f:
                f.write(content)
            
            logger.info(f"✅ 文件已保存: {filepath}")
            return True
            
        except Exception as e:
            logger.error(f"❌ 文件写入失败: {e}")
            return False
    
    @staticmethod
    def read_file_safe(filepath: str) -> Optional[str]:
        """安全读取文件"""
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                return f.read()
        except Exception as e:
            logger.error(f"❌ 文件读取失败 {filepath}: {e}")
            return None

class TestRunner:
    """测试运行器"""
    
    @staticmethod
    def run_pytest(test_dir: str, timeout: int = 30) -> Dict[str, Any]:
        """运行pytest测试"""
        try:
            result = subprocess.run(
                ["python", "-m", "pytest", test_dir, "-v", "--tb=short"],
                capture_output=True,
                text=True,
                timeout=timeout,
                cwd=os.getcwd()
            )
            
            return {
                'success': result.returncode == 0,
                'stdout': result.stdout,
                'stderr': result.stderr,
                'return_code': result.returncode
            }
            
        except subprocess.TimeoutExpired:
            return {
                'success': False,
                'stdout': '',
                'stderr': f'测试超时 ({timeout}秒)',
                'return_code': -1
            }
        except Exception as e:
            return {
                'success': False,
                'stdout': '',
                'stderr': str(e),
                'return_code': -1
            }
    
    @staticmethod
    def run_code_in_sandbox(code: str, timeout: int = 10) -> Dict[str, Any]:
        """在沙箱环境中运行代码"""
        with tempfile.TemporaryDirectory() as tmpdir:
            code_file = os.path.join(tmpdir, "main.py")
            
            try:
                with open(code_file, 'w', encoding='utf-8') as f:
                    f.write(code)
                
                result = subprocess.run(
                    ["python", code_file],
                    capture_output=True,
                    text=True,
                    timeout=timeout,
                    cwd=tmpdir
                )
                
                return {
                    'success': result.returncode == 0,
                    'output': result.stdout,
                    'error': result.stderr,
                    'return_code': result.returncode
                }
                
            except subprocess.TimeoutExpired:
                return {
                    'success': False,
                    'output': '',
                    'error': f'代码执行超时 ({timeout}秒)',
                    'return_code': -1
                }
            except Exception as e:
                return {
                    'success': False,
                    'output': '',
                    'error': str(e),
                    'return_code': -1
                }

def format_code_stats(stats: Dict[str, Any]) -> str:
    """格式化代码统计信息"""
    if 'error' in stats:
        return f"❌ 分析失败: {stats['error']}"
    
    return f"""
📊 **代码统计**
• 总行数: {stats.get('lines', 0)}
• 函数数量: {stats.get('functions', 0)}
• 类数量: {stats.get('classes', 0)}
• 导入语句: {stats.get('imports', 0)}
• 复杂度评分: {stats.get('complexity_score', 0)}
"""

def create_progress_bar(description: str = "处理中..."):
    """创建进度条"""
    return Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
        transient=True
    )

def display_results_table(results: Dict[str, Any]):
    """显示结果表格"""
    table = Table(title="🤖 CodeBuddy 执行结果")
    table.add_column("阶段", style="cyan", no_wrap=True)
    table.add_column("状态", style="green")
    table.add_column("详情", style="yellow")
    
    for stage, result in results.items():
        status = "✅ 成功" if result.get('success', True) else "❌ 失败"
        details = str(result.get('summary', ''))[:50] + "..." if len(str(result.get('summary', ''))) > 50 else str(result.get('summary', ''))
        table.add_row(stage, status, details)
    
    console.print(table) 